:- module(database_files,[load_database/1,save_database/1,save_element/2],[assertions]).

:- use_module(library(write)).
:- use_module(library(lists)).
:- use_module(library(read),[read/2]).
:- use_module(library(write),[write/2]).

:- use_module(db(resource_access)).
:- use_module(db(asset_access)).
:- use_module(db(dictionary_access)).
:- use_module(db(organization_access)).
:- use_module(db(database_access)).
:- use_module(db(user_access)).
:- use_module(db(certification_access)).

:- comment(title, "Application-Oriented Database Interface").
:- comment(subtitle, "AMOS Project").

:- comment(author, "Jes@'{u}s Correas, Manuel Carro, and the CLIP Group").
:- comment(author, "Facultad de Inform@'{a}tica").
:- comment(author, "Universidad Polit@'{e}cnica de Madrid").
:- comment(author, "@tt{clip@@dia.fi.upm.es}").
:- comment(author, "@tt{http://www.clip.dia.fi.upm.es/}").

:- comment(module, "

This module implements the predicates related to storing and
recovering database data from external files. Specifically, it
contains predicates for saving a copy of all the operations performed
in the database, as well as a way to load or store the complete
database externally at a high level.

").

%% ==================================================================

:- pred save_elements(out(Mode)): atm

# "Determines if database updates backup is active or not. @var{Mode}
  can be @code{on} or @code{off}.".

save_elements(on).
%save_elements(off).

%% ==================================================================

:- pred load_database(in(File)): file

# "Loads the data stored in @var{File} in the Amos database. As the
  data in @var{File} is stored using save_database/1 or
  save_element/1, internal keys are regenerated, and several
  incremental database loads are allowed.".

load_database(File):-
	catch(
		 (
		     open(File,read,S),
		     repeat,
		     read(S,Data),
		     ( Data = end_of_file ->
		       true
		     ;
		       load_element(Data),
		       fail
		     ),
		     close(S)
		 ),
		 Error,
		 err_msg(Error)
	     ).

load_element(Data):-
	(Data = a(_Date,_Key,Element) ; Data = u(_Date,_Key,Element)), !,
	db_update(Element).
load_element(r(_Date,_Key,Element)):-
	db_delete(Element).

db_update(Data):-
	resource(Data),!,
	resource_db_update(_,Data).
db_update(Data):-
	organization(Data),!,
	organization_db_update(_,Data).
db_update(Data):-
	user(Data),!,
	user_db_update(_,Data).
db_update(Data):-
	certification(Data),!,
	certification_db_update(_,Data).
db_update(Data):-
	ditem(Data),!,
	ditem_db_update(_,Data).

db_delete(Data):-
	resource(Data),!,
	resource_delete(Data).
db_delete(Data):-
	organization(Data),!,
	organization_delete(Data).
db_delete(Data):-
	user(Data),!,
	user_delete(Data).
db_delete(Data):-
	certification(Data),!,
	certification_delete(Data).
db_delete(Data):-
	ditem(Data),!,
	ditem_delete(Data).


%% ==================================================================

:- pred save_database(in(File)): file

# "Saves the data stored in the Amos database to @var{File}. This
  predicate uses high-level structures to store the data, so
  incremental database loads are allowed. The basic database elements
  that are currently stored are resources and dictionary items (and
  every other element referenced by these.)".

save_database(File):-
	open(File,write,S),
	save_resources(S),
	save_ditems(S),
	close(S).

save_resources(S):-
	resource_description(Res,Key),
	save_element_on_stream(a(Key,Res),S),
	fail.
save_resources(_).

save_ditems(S):-
	ditem_description(Ditem,Key),
	save_element_on_stream(a(Key,Ditem),S),
	fail.
save_ditems(_).

save_element_on_stream(Data,Stream):-
	Data =.. [Op,Key,Item],
	datime(Date),
	LogData =.. [Op,Date,Key,Item],
	writeq(Stream,LogData),
	display(Stream,'.'),
	nl(Stream),
	!.

%% ==================================================================

:- use_module(amos(configuration),[log_dir/1,interm_file_perms/1]).
:- use_module(library(system),[datime/1,chmod/2]).
:- use_module(library(write),[writeq/2,write/2]).

:- pred save_element(in(File),in(LogTerm)): file * log_term

# "Keeps a log file with all the operations which modify the
  database. @var{LogTerm} is a term that contain the key and a
  high-level description of the element that has been modified in the
  database. In the log file the term is stored adding the current date
  and time as first argument.  @var{File} is stored in the directory
  defined in @tt{configuration.pl} file (predicate @tt{log_dir}.)".

save_element(_FileName,_Data):-
	save_elements(off), !.
save_element(FileName,Data):-
	log_dir(Dir),
	get_complete_file_name(Dir,FileName,File),
	interm_file_perms(Perm0),
	convert_octal(Perm0,Perm),
	catch(
		 (
		     open(File,append,S),
		     save_element_on_stream(Data,S),
		     close(S),
		     chmod(File,Perm)
		 ),
		 Error,
		 err_msg(Error)
	     ).

get_complete_file_name(Dir,FileName,File):-
	append_slash(Dir,DirB),
	atom_concat(DirB,FileName,File).

convert_octal(Perm0,Perm):-
	number_codes(Perm0,PermC),
	convert_octal_1(PermC,0,Perm).

convert_octal_1([],Perm,Perm).
convert_octal_1([C|Cs],Perm0,Perm):-
	Perm1 is Perm0 << 3 + (C - 0'0),
	convert_octal_1(Cs,Perm1,Perm).
	

err_msg(Err):-
	write(user_error,'ERROR: writing database log: '),
	write(user_error,Err),
	nl(user_error).

append_slash(Dir,Dir):-
	atom_concat(_,'/',Dir),!.
append_slash(Dir,DirSlash):-
	atom_concat(Dir,'/',DirSlash).

log_term(a(_Date,_Data)).
log_term(u(_Date,_Data)).
log_term(r(_Date,_Data)).


%% ---------------------------------------------------------------------


:- comment(version_maintenance,on). 


:- comment(version(0*1+0,2003/07/16,10:57*07+'CEST'), "Initial
   version: database logging and complete database saving/recovering.
   (Jesus Correas Fernandez)").

